from __future__ import division
import math

from PyQt4.QtCore import *
from PyQt4.QtGui import *

from geolib.geom2d import Bounds
from qpynygeolib.maplayers import LayerFactory
from .tiles import TiledBitmapLayer

class OpenStreetMapLayerFactory(LayerFactory):
    
    def __init__(self):
        super(OpenStreetMapLayerFactory, self).__init__()
        self.__types = ("MapFrameLayer|BitmapLayer|TiledBitmapLayer|SlippyTileLayer|OpenStreetMapLayer",
                        "MapFrameLayer|BitmapLayer|TiledBitmapLayer|SlippyTileLayer|MapQuestLayer",
                        "MapFrameLayer|BitmapLayer|TiledBitmapLayer|SlippyTileLayer|MapQuestOpenAerialLayer")
    
    def layerTypes(self):
        out = list(self.__types)
        self.appendChildFactoryLayerTypes(out)
        return out
    
    def newLayer(self, layerType, layerId, options={}):
        if layerType == self.__types[0]:
            return OpenStreetMapLayer(layerId)
        elif layerType == self.__types[1]:
            return MapQuestLayer(layerId)
        elif layerType == self.__types[2]:
            return MapQuestOpenAerialLayer(layerId)
        else:
            return self.newLayerFromChildFactory(layerType, layerId, options)


class SlippyTileLayer(TiledBitmapLayer):
    
    def __init__(self, layerId, tileType, cacheDir=None, zIndex=0):
        super(SlippyTileLayer, self).__init__(layerId, tileType, cacheDir, zIndex)
        
    def getTiles(self, latLonBounds, zoom):
        tilesOut = []
        for bounds in latLonBounds.splitToValidBounds():
            nw = self.__tile(bounds.topLeft(), zoom)
            se = self.__tile(bounds.bottomRight(), zoom)
            for xtileIndex in range(nw[0], se[0]+1):
                for ytileIndex in range(nw[1], se[1]+1):
                    tilesOut.append((xtileIndex, ytileIndex, int(zoom)))
        return tilesOut
    
    def type(self):
        return "MapFrameLayer|BitmapLayer|TiledBitmapLayer|SlippyTileLayer"
    
    def tileBounds(self, tile):
        return Bounds.fromPoints(self.__nwCorner(tile), self.__nwCorner((tile[0]+1, tile[1]+1, tile[2])))
    
    def minZoom(self):
        return 0
    
    def maxZoom(self):
        return 18
    
    def __maxtiles(self, zoom):
        return int(2.0 ** zoom)
    
    def __tile(self, point, zoom):
        maxTile = self.__maxtiles(zoom)
        n = 2.0 ** zoom
        lon, lat = point
        xtile = int((lon + 180.0) / 360.0 * n)
        if lat >= 90:
            ytile = 0
        elif lat <= -90:
            ytile = maxTile
        else:
            lat_rad = math.radians(lat)
            ytile = int((1.0 - math.log(math.tan(lat_rad) + (1 / math.cos(lat_rad))) / math.pi) / 2.0 * n)
        if xtile > maxTile:
            xtile = maxTile
        elif xtile < 0:
            xtile = 0
            
        return (xtile, ytile)
    
    def __nwCorner(self, tile):
        n = 2.0 ** tile[2]
        lon_deg = tile[0] / n * 360.0 - 180.0
        lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * tile[1] / n)))
        lat_deg = math.degrees(lat_rad)
        return (lon_deg, lat_deg)

class OpenStreetMapLayer(SlippyTileLayer):
    
    URL_SERVERS = ["http://a.tile.openstreetmap.org",
                    "http://b.tile.openstreetmap.org",
                    "http://c.tile.openstreetmap.org"]
    
    def __init__(self, layerId, cacheDir=None, zIndex=0):
        super(OpenStreetMapLayer, self).__init__(layerId, "openstreetmap", cacheDir, zIndex)
    
    def type(self):
        return "MapFrameLayer|BitmapLayer|TiledBitmapLayer|SlippyTileLayer|OpenStreetMapLayer"
    
    def tileUrls(self, tile):
        endString = "/%i/%i/%i.png" % (tile[2], tile[0], tile[1])
        return [server + endString for server in OpenStreetMapLayer.URL_SERVERS]

class MapQuestLayer(SlippyTileLayer):
    
    URL_SERVERS = ["http://otile1.mqcdn.com",
                   "http://otile2.mqcdn.com",
                   "http://otile3.mqcdn.com",
                   "http://otile4.mqcdn.com"]
    
    def __init__(self, layerId, cacheDir=None, zIndex=0):
        super(MapQuestLayer, self).__init__(layerId, "mapquest", cacheDir, zIndex)
    
    def type(self):
        return "MapFrameLayer|BitmapLayer|TiledBitmapLayer|SlippyTileLayer|MapQuestLayer"
    
    def tileUrls(self, tile):
        endString = "/tiles/1.0.0/osm/%i/%i/%i.jpg" % (tile[2], tile[0], tile[1])
        return [server + endString for server in MapQuestLayer.URL_SERVERS]
    
    def fileName(self, tile):
        return "%s_%i_%i_%i.jpg" % (self.tileType(), tile[2], tile[0], tile[1])
    
    def maxZoom(self):
        return 19
   
class MapQuestOpenAerialLayer(SlippyTileLayer):
    
    def __init__(self, layerId, cacheDir=None, zIndex=0):
        super(MapQuestOpenAerialLayer, self).__init__(layerId, "mapquestopenaerial", cacheDir, zIndex)
    
    def type(self):
        return "MapFrameLayer|BitmapLayer|TiledBitmapLayer|SlippyTileLayer|MapQuestOpenAerialLayer"
    
    def fileName(self, tile):
        return "%s_%i_%i_%i.jpg" % (self.tileType(), tile[2], tile[0], tile[1])
    
    def tileUrls(self, tile):
        endString = "/tiles/1.0.0/sat/%i/%i/%i.jpg" % (tile[2], tile[0], tile[1])
        return [server + endString for server in MapQuestLayer.URL_SERVERS]
    
    def maxZoom(self):
        return 12
    

    